﻿using Algorithm.Check;
using Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Channels;
using static Algorithm.Check.ExtensionForCRC16;

namespace NewDevice.RS485
{
    /// <summary>
    /// PQW_IO继电器模块控制类
    /// 提供基于Modbus协议的继电器控制功能
    /// </summary>
    [Obsolete("使用PQW_IO_V2代替")]
    public class PQW_IO
    {
        private string deviceName;
        private readonly SerialBase serial;
        private readonly object locker = new object();

        // Modbus 功能码
        private const byte ReadDOStatusFunctionCode = 0x01;  // 读取输出状态功能码

        private const byte ReadDIStatusFunctionCode = 0x02;  // 读取输入状态功能码
        private const byte WriteSingleCoilFunctionCode = 0x05;  // 写单个线圈功能码
        private const byte WriteSingleRegisterFunctionCode = 0x06;  // 写单个寄存器功能码
        private const byte WriteMultipleCoiFunctionCode = 0x0F;  // 写多个线圈功能码

        // 寄存器地址
        private const int OffAllRegisterAddress = 0x85;  // 关闭所有继电器的寄存器地址

        /// <summary>
        /// 数字输入状态字典
        /// Key: 从机ID, Value: 输入状态字节数组
        /// </summary>
        public ConcurrentDictionary<int, byte[]> DIStatus { get; set; } = new ConcurrentDictionary<int, byte[]>();

        /// <summary>
        /// 数字输出状态字典
        /// Key: 从机ID, Value: 输出状态字节数组
        /// </summary>
        public ConcurrentDictionary<int, byte[]> DOStatus { get; set; } = new ConcurrentDictionary<int, byte[]>();

        /// <summary>
        /// 数字输入通道数量
        /// </summary>
        public int DICount { get; set; } = 4;

        /// <summary>
        /// 数字输出通道数量
        /// </summary>
        public int DOCount { get; set; } = 4;

        private int slaveId;

        /// <summary>
        /// di update event
        /// </summary>
        public Action<byte[]> DIUpdateEvent;

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="bus"></param>
        /// <param name="slaverId"></param>
        /// <param name="devName"></param>
        public PQW_IO(SerialBase bus, int slaverId, string devName = "PQW_IO继电器模块")
        {
            serial = bus;
            this.slaveId = slaverId;
            this.deviceName = devName;
        }

        /// <summary>
        /// 获取指定从机的所有数字输入状态
        /// </summary>
        public void GetDI()
        {
            int channelIndex = 0;
            byte[] requestData = new byte[] {
                (byte)slaveId,
                ReadDIStatusFunctionCode,
                (byte)((channelIndex & 0xff00) >> 8),
                (byte)(channelIndex & 0xff),
                (byte)((DICount & 0xff00) >> 8),
                (byte)(DICount & 0xff)
            };

            byte[] response = SendCommand(requestData, 5 + ((DICount + 8 - 1) / 8));
            var diStaus = response.Skip(3).Take(response[2]).ToArray();
            DIStatus.AddOrUpdate(slaveId, diStaus, (key, oldValue) => diStaus);
            DIUpdateEvent?.Invoke(diStaus);
        }

        /// <summary>
        /// 获取指定从机和通道的数字输入状态
        /// </summary>
        /// <param name="channelIndex">通道索引</param>
        /// <returns>通道状态</returns>
        /// <exception cref="Exception">当从机ID不存在时抛出异常</exception>
        public bool GetDI(int channelIndex)
        {
            if (DIStatus.TryGetValue(slaveId, out var value))
            {
                var sts = (value[channelIndex / 8] & (1 << channelIndex % 8)) > 0;
                return sts;
            }
            throw new Exception($"{deviceName} slaveId[{slaveId}] not contained");
        }

        /// <summary>
        /// 获取指定从机和通道的数字输出状态
        /// </summary>
        /// <param name="channelIndex">通道索引</param>
        /// <returns>通道状态</returns>
        /// <exception cref="Exception">当从机ID不存在时抛出异常</exception>
        public bool GetDO(int channelIndex)
        {
            if (DOStatus.TryGetValue(slaveId, out var value))
            {
                var sts = (value[channelIndex / 8] & (1 << channelIndex % 8)) > 0;
                return sts;
            }
            throw new Exception($"{deviceName} slaveId[{slaveId}] DO not contained");
        }

        /// <summary>
        /// 获取指定从机的所有数字输出状态
        /// </summary>
        public void GetDO()
        {
            int channelIndex = 0;
            byte[] requestData = new byte[] {
                (byte)slaveId,
                ReadDOStatusFunctionCode,
                (byte)((channelIndex & 0xff00) >> 8),
                (byte)(channelIndex & 0xff),
                (byte)((DOCount & 0xff00) >> 8),
                (byte)(DOCount & 0xff)
            };

            byte[] response = SendCommand(requestData, 5 + ((DOCount + 8 - 1) / 8));
            var doStaus = response.Skip(3).Take(response[2]).ToArray();
            DOStatus.AddOrUpdate(slaveId, doStaus, (key, oldValue) => doStaus);
        }

        /// <summary>
        /// 设置数字输出通道状态
        /// </summary>
        /// <param name="channel">通道索引</param>
        /// <param name="status">要设置的状态</param>
        public void SetDO(int channel, bool status)
        {
            int channelIndex = channel;
            byte[] requestData = new byte[] {
                (byte)slaveId,
                WriteSingleCoilFunctionCode,
                (byte)((channelIndex & 0xff00) >> 8),
                (byte)(channelIndex & 0xff),
                (byte)(status ? 0xff : 0x00),
                0x00
            };

            SendCommand(requestData, requestData.Length + 2);
        }

        /// <summary>
        /// 关闭指定从机的所有继电器
        /// </summary>
        public void OffAll()
        {
            byte[] requestData = new byte[] {
                (byte)slaveId,
                WriteSingleRegisterFunctionCode,
                0x00,
                OffAllRegisterAddress,
                0x00,
                0x00
            };

            SendCommand(requestData, requestData.Length);
        }

        /// <summary>
        /// 发送Modbus命令并接收响应
        /// </summary>
        /// <param name="requestData">请求数据</param>
        /// <param name="expectedResponseLength">期望的响应长度</param>
        /// <returns>响应数据</returns>
        /// <exception cref="Exception">当响应长度不符合预期时抛出异常</exception>
        private byte[] SendCommand(byte[] requestData, int expectedResponseLength)
        {
            byte[] command = requestData.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            byte[] response = serial.SendBytesAndWaitResponse(command, ReceivedDataCheck.FuncModbusCRC16Check);

            if (response.Length != expectedResponseLength)
            {
                throw new Exception($"[{deviceName}] 返回数据长度异常，期望长度: {expectedResponseLength}, 实际长度: {response.Length}");
            }

            return response;
        }
    }
}